
/* ****************************************************************** 
   File name: C_Log_Writer.h
   Associated Files necessary to complete this utility:
   C_Log_File_Writer.cpp
   C_TCP_Format_WSA_Text.h
   C_TCP_Format_WSA_Text.cpp


********************************************************************* */


/* ******************************************************************
    (C) Copyright 2012/2013 Bryan Kelly
    This file is part of the software utility named:
    Bryan Kelly's Utility Kit.

    BK Utility Kit is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    The BK Utility Kit is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with the BK Utility Kit.  If not, see <http://www.gnu.org/licenses/>.

 
********************************************************************* */


/* ***************************************************************************
Class C_Log_Writer
file name: C_Log_Writer.h

A utility that writes information to a log file. Unicode.
C:\LOG_FILES   This is the default and hard coded location for the log files.
               Even if not used, be advised to create the directory, just in case.
               I use E:\LOG_FILES

Minimal Use:  
   Instantiate the class and it opens the log file.
   Call: Write_Log_File_Entry( your_WCHAR_string ) To write an entry.  Size limit 400 characters
   Delete the class.  The destructor posts a closing entry, closes the file, and exits

   Every entry is prefixed by the time: HH:MM:SS.xxx  down to the millisecond

Those are the essentials.  More detail follows. 
 
This class opens a log file and accepts text to write to the file.
Log file name begins with a prefix provided by the owner (calling application)
followed by the time it is opened. This enables multiple applications to have 
log files open simultaneously.
To prevent excessivly large files a new log file is automatically opened every 
LOG_FILE_RESTART_COUNT entries.

Look at this method:  Show_Method_Status(...)
A user app can call this and get some detailed log entries with a minimum of fuss and
code within the user app.  Users encouraged to feel free to add various versions of this function
to help with code development and testing.

Caution:  Take care to delete log files.
For real time monitoring, the user can open a console window.  Data sent to the logger is output
to console and/or file.  The output to each can be enabled and disabled separately.

*********************************************************************** */

#pragma once
#include <wchar.h>
#include <windows.h>
#include "WinNT.h"
#include "C_TCP_Format_WSA_Text.h"

#include <errno.h>

namespace FWSA = NS_C_TCP_FORMAT_WSA;
using namespace FWSA;


   // Provide a common enumeration for controling the amount of logging.
   // Intended for use by the owning application, not used within this logger class.
enum   ENUM_LOGGING_LEVEL
{
    LOGGING_NONE,
    LOGGING_ERRORS_ONLY,
    LOGGING_LOW,
    LOGGING_MEDIUM,
    LOGGING_VERBOSE
};

     // Sometimes success is 0, sometimes, non-zero,
      // sometimes non negative, sometimes specific values.
      // found this value here: http://msdn.microsoft.com/en-us/library/z5hh6ee9.aspx
      // and declared the constant so I won't forget.
   const errno_t ZERO_IS_SUCCESS = 0;

   const BOOL    ZERO_IS_ERROR    = 0;

   const int     FCLOSE_SUCCEEDED = 0;

      // http://msdn.microsoft.com/en-us/library/t33ya8ky.aspx
      // This says: "Each of these functions returns a nonnegative value if it is successful."
      // That includes zero.  So test for < ZERO_OR_GREATER_IS_SUCCESS to detect errors.
   const int     ZERO_OR_GREATER_IS_SUCCESS = 0;

   const bool    DIRECTORY_DOES_NOT_EXIST = FALSE;

   const bool    WRITE_CONSOLE_ERROR = 0;

      // A constant for the optional argument to the method that closes the log file.
      // The default is APPLICATION_IS_CLOSING so the user need no argument to the
      // close statement.  The destructor uses DESTRUCTOR_IS_CLOSING.  In this manner
      // the user can determine who closed the file.
   const bool    APPLICATION_IS_CLOSING = false;
   const bool    DESTRUCTOR_IS_CLOSING  = true;

      // true and false work just find and are clear.  I just like descriptive names
      // rather than true and false.
   const bool    ENABLE_WRITES          = true;
   const bool    DISABLE_WRITES         = false;

   const bool    ENABLE_AUTOMATIC_RESTARTS  = true;
   const bool    DISABLE_AUTOMATIC_RESTARTS = false;

    // If application does not supply a directory name, use this one.
const WCHAR DEFAULT_DlRECTORY[ 16 ] = L"C:\\LOG_FILES\\";

   // Multiple application can use this utility simultaneously.  The user can
   // provide a prefix to identify each log.  This is the default prefix.  
   // It is up to the user to keep the names unique.
const WCHAR  DEFAULT_FILENAME_PREFIX[]      = L"Log_File_";
const size_t LENGTH_DEFAULT_FILENAME_PREFIX = wcslen( DEFAULT_FILENAME_PREFIX );

const WCHAR OPEN_FILE_NOTICE[]       = L"Log File Opened";
const size_t LENGTH_OPEN_FILE_NOTICE = wcslen( OPEN_FILE_NOTICE );

const WCHAR APPLICATION_CLOSE_FILE_NOTICE[]    = L"Application is closing the log file";
const size_t  APPLICATION_LENGTH_CLOSE_NOTICE  = wcslen( APPLICATION_CLOSE_FILE_NOTICE );

const WCHAR DESTRUCTOR_CLOSE_FILE_NOTICE[]    = L"Destructor is closing the log file";
const size_t  DESTRUCTOR_LENGTH_CLOSE_NOTICE  = wcslen( DESTRUCTOR_CLOSE_FILE_NOTICE );

const WCHAR START_NEW_FILE_NOTICE[]         = L"C_Log_Writer starting new log file";
const size_t   LENGTH_START_NEW_FILE_NOTICE = wcslen( START_NEW_FILE_NOTICE );

const WCHAR TRUNCATION_WARNING[]          = L"Warning, next line was too long, truncated.";
const size_t   LENGTH_TRUNCATION_WARNING  = wcslen( TRUNCATION_WARNING );

const WCHAR OPENED_CONSOLE_NOTICE[]       = L"Opened the Console window.";
const size_t LENGTH_OPENED_CONSOLE_NOTICE = wcslen( OPENED_CONSOLE_NOTICE );

const WCHAR CLOSING_CONSOLE_NOTICE[]       = L"Closing the Console window.";
const size_t LENGTH_CLOSING_CONSOLE_NOTICE = wcslen( CLOSING_CONSOLE_NOTICE );

const WCHAR SUPPLIED_STRING_TOO_SMALL[]    = L"The supplied string was too small";
const size_t LENGTH_SUPPLIED_STRING_TOO_SMALL = wcslen( SUPPLIED_STRING_TOO_SMALL );

   // Prevent the log file from growing too large.  After this many 
   // entries have been posted, automatically start another log file.
const unsigned int LOG_FILE_RESTART_COUNT = 20000;

   // Set some limit constants.  The names should be self explanatory.
const size_t MAX_FILE_NAME_LENGTH = 60;  // A somewhat arbitrary value, but see MAX_PATH just below
const size_t MAX_DATE_TIME_LENGTH = 25;  // _2013_12_31-HH-MM-SS.000  =  24 characters + NULL

   // file name prefix supplied by the user to provide each log file with a unique
   // and identifiable prefix.  The prefix + the time are limited to MAX_FILE_NAME_LENGTH
const size_t MAX_FILE_NAME_PREFIX_LENGTH    = MAX_FILE_NAME_LENGTH - MAX_DATE_TIME_LENGTH ;

   // MAX_PATH is a Microsoft declaration and was found here:  
   //    http://msdn.microsoft.com/en-us/library/aa365247.aspx, 260 characters.
const size_t MAX_DIRECTORY_NAME_LENGTH      = MAX_PATH - MAX_FILE_NAME_LENGTH;

const size_t MAX_FULL_PATH_AND_NAME         = MAX_PATH;

   // MAX_PATH is 260.  Add an arbitrary number of characters to produce a length.
   // WHY:  Presume ther user provides a very long path name and that name is not valid.
   // The log utility needs to log that entrie name along with some test that describes
   // the problems.  Hopefully this length provides for that. 
const size_t MAX_LOG_STRING_LENGTH        = 400;  // arbitrary max log entry.

   // Max internal log entry length.  The max length of a string that can be written
   // to the file log or to the console.  This is the max user length plus the internal
   // overhead needed for the time stamp.
const size_t MAX_INTERNAL_LOG_LENGTH = MAX_LOG_STRING_LENGTH + MAX_DATE_TIME_LENGTH;

class C_Log_Writer
{
public:

      // Startup constructor.
      // Arg 1:  Directory where the log files are written.
      // Arg 2:  File name prefix put in front of date and time to identify
      //         the application writing the log.
      // If NULLs are provided, default values are used.
   C_Log_Writer( const WCHAR *new_directory_name, const WCHAR *new_name_prefix );

   ~C_Log_Writer(void);

        // Provide the ability to change this stuff during operation.
   bool Set_Prefix_String(    const WCHAR *new_prefix_string    );

       // The user can temporarily change the prefix then return to the previous one.
       // Depth is only one.
   void Restore_Previous_Prefix( );

       // Determine the directory where the log files are placed.  If the user submits a faulty
       // directory, an error is logged and the previous value is restored.
   bool Set_Directory_String( const WCHAR *new_directory_string );

   bool Open_Log_File( );

      // Call anytime desired.  Automatically called after LOG_FILE_RESTART_COUNT log writes.
   bool Re_Open_Log_File();

       // The user app can/should ignore the optional argument.  
       // The destructor overrides the default so the user can determine who closed the log file.
   bool Close_Log_File( bool who_is_closer = APPLICATION_IS_CLOSING );

   bool Is_Log_File_Open();

   bool Open_Console_Window();
   bool Close_Console_Window();
   bool Is_Console_Window_Open();

        // Enable and disable the outputs.
   void Set_Log_File_Write_Enable_Flag( bool enable );
   bool Get_Log_File_Write_Enable_Flag( );
   void Set_Console_Output_Enable_Flag( bool new_value );
   bool Get_Console_Output_Enable_Flag( );

        // If the user does not want automatic restarts on the log files,
        // this will do it.
   void Set_Automatic_Restart_Flag( bool new_value );
   bool Get_Automatic_Restart_Flag( );

 

      // The primary user interface.  This function adds a time prefix and
      // logs the data.
   bool Write_Log_File_Entry( const WCHAR *data_to_log );


   // A pre-set formatting function for logging the status of a method.
   // The caller just hands over specific variables to log.
   // This logger does all the grunt work to format it with labels
   // saving the caller from that hassle.
void C_Log_Writer::Show_Method_Status( const WCHAR *method_name, 
                                       WCHAR       *optional_phrase, 
                                       bool         method_status,
                                       int          wsa_error );

private:

        // Instantiate this class to translate from error code to text.
    C_TCP_Format_WSA_Text   m_C_TCP_Format_WSA_Text;

        // When opening/re-opening a log file, create the name of the new file
        // based on the prefix and the time.
   void Build_Log_File_Name();

   bool Write_To_Console( const WCHAR *data_to_log );
   bool Write_To_File(    const WCHAR *data_to_log );


       // The status of this class.  The caller can request the status at any time.
   bool m_log_writer_status;

     // Count the number of log entries made. Open another log file every 
     // LOG_FILE_RESTART_COUNT entries to keep the size managable.
   unsigned int m_log_entry_count;

     // Enable or disable all data logging
   bool m_file_output_enabled;

   bool m_auto_restart;

        // Function Write_Log_File_Entry() can call itself in the even of
        // an error.  This limits the recursion depth.
   unsigned int m_recursion_depth;

     // enable or disable console output
   bool m_console_output_enabled;
   HANDLE m_console_handle;

   SYSTEMTIME m_time_of_day;

   FILE       *mp_log_FILE;


        // The name of each log file created is prefixed with this string.
        // The user sets this to a unique value for each application and thread.
   WCHAR m_prefix_string[    MAX_FILE_NAME_PREFIX_LENGTH ];

        // Provides for the ability to create a temporary log file
        // with a special prefix, then return to the standard prefix.
   WCHAR m_previous_prefix[  MAX_FILE_NAME_PREFIX_LENGTH ];

       // The directory where the log files are written.
   WCHAR m_directory_string[ MAX_DIRECTORY_NAME_LENGTH ];

        // Finally, the full completed name of the logging file is written
        // to this string and the file opened with it.
   WCHAR m_full_path_and_name[ MAX_FULL_PATH_AND_NAME ];


        // Keep the last error and text here.  If things go bad the owner
        // can make a function call and get this data.
   int   m_get_last_error;
   WCHAR m_text_get_last_error[ MAX_WSA_ERROR_TEXT ];


};